﻿using System.Drawing;
using md.imi.membranes.objects.Gui;

namespace md.imi.membranes.objects.membranes
{
    using System;
    using System.Collections.Generic;
    using System.Xml.Serialization;
    using md.imi.membrane.constants;
    using md.imi.membranes.objects.psystems;
    using md.imi.membranes.objects.rules;
    using md.imi.membranes.objects.strings;

    /// <summary>
    ///     Membrane description. Contains all information related to a concrete membrane
    /// </summary>
    [Serializable]
    public class Membrane : CoreObject, IDisposable
    {
        /// <summary>
        ///     Gets or Sets the stribg objects list contained in this membrane
        /// </summary>
        public List<StringObject> Multiset { get; set; }

        /// <summary>
        ///     Gets or Sets the textual membrane label
        /// (different membranes may have the same label)
        /// </summary>
        public String Label { get; set; }

        /// <summary>
        ///     Gets or Sets the GUId which unically identifies a membrane
        /// </summary>
        public Guid Id { get; set; }			// GUID - UNICAL

        /// <summary>
        ///     Gets or Sets the charge of the current memebrane
        /// </summary>
        public Charge Charge { get; set; }

        /// <summary>
        ///     Gets or Sets a list of rules attached to this membrane
        /// </summary>
        public List<Rule> Rules { get; set; }

        /// <summary>
        ///     Gets or Sets the parent PSystem
        /// </summary>
        [XmlIgnore]
        public PSystemsTree Configuration { get; set; }

        /// <summary>
        ///     Information which reflects the propserties of visual object
        /// </summary>
        public GuiInfo GuiInfo { get; set; }

        /// <summary>
        ///     Tag variable
        /// </summary>
        public object Tag { get; set; }

        /// <summary>
        ///     Default constructor
        /// </summary>
        public Membrane()
        {
            Multiset = new List<StringObject>();
            Label = "0";
            Id = Guid.NewGuid();
            Charge = Charge.Neutral;
            Rules = new List<Rule>();
            GuiInfo = new GuiInfo(new Point(10, 10), new Size(100, 100));
        }

        /// <summary>
        ///     Default constructor, initialize properties with default values
        /// </summary>
        public Membrane(PSystemsTree root)
            : this()
        {
            Configuration = root;
        }

        /// <summary>
        ///     Copy Constructor
        /// </summary>
        /// <param name="parent"></param>
        public Membrane(Membrane parent)
            : this()
        {

            // copy multisets
            this.Multiset = new List<StringObject>();
            foreach (var ms in parent.Multiset)
            {
                this.Multiset.Add(new StringObject(ms));
            }


            this.Label = parent.Label;
            this.Id = new Guid(parent.Id.ToString());
            this.Charge = parent.Charge;

            // copy rules
            this.Rules = new List<Rule>();
            foreach (var rule in parent.Rules)
            {
                this.Rules.Add(rule.GetCopy());
            }

            this.Configuration = parent.Configuration;
            this.GuiInfo  = new GuiInfo(parent.GuiInfo);
            this.Tag = parent.Tag;
        }


        /// <summary>
        /// 	Calculates available solutions for each string object in  membrane
        /// For this purpose, the string objects ara taken one by one, and are used
        /// all rules from this membrane to calculate possible solutions.
        /// </summary>
        /// <returns></returns>
        public List<List<Solution>> CalculateSolutions(List<List<Solution>> listRes)
        {
            /*
             *
             *  List1 = СПИСОК(список(элемент),список(элемент), ...)
             *  ListRes  = СПИСОК(список(элемент, ...элемент),список(элемент, ...элемент), ...)
             */

            var newListRes = new List<List<Solution>>();

            newListRes.AddRange(listRes);

            Console.WriteLine("1. iterate through all StringObjects");
            
            // 1. iterate through all StringObjects);
            for (int i = 0; i < Multiset.Count; i++)
            {
                var list1 = new List<List<Solution>>();
                Console.WriteLine("// 2. use all rules for each string : " + Multiset[i]);
                
                // 2. use all rules for each string
                foreach (var rule in Rules)
                {
                    var newSolutions = rule.CalculateAvailableSolutions(this, i);
                    if (newSolutions == null || newSolutions.Count == 0) continue; // no solution was fond for this string and rule
                    foreach (var item in newSolutions)
                    {
                        list1.Add(new List<Solution>());
                        list1[list1.Count - 1].Add(item);
                    }
                }

                if (list1.Count > 0)
                {
                    if (newListRes.Count == 0)
                        newListRes = new List<List<Solution>>(list1);
                    else
                        newListRes = Compose(list1, newListRes);
                }
            }

            return newListRes;
        }

        /// <summary>
        ///
        /// </summary>
        /// <param name="list1"></param>
        /// <param name="listRes"></param>
        /// <returns></returns>
        private List<List<Solution>> Compose(List<List<Solution>> List1, List<List<Solution>> ListRez)
        {
            List<List<Solution>> listR = new List<List<Solution>>();

            foreach (var elList1 in List1)
            {
                foreach (var elListRez in ListRez)
                {
                    var elListR = new List<Solution>();
                    
                     listR.Add(elListR); // grow in height

                     listR[listR.Count - 1].AddRange(elList1); 	// grow in wide
                     listR[listR.Count - 1].AddRange(elListRez); // grow in wide
                }
            }

            return listR;
        }

        /// <summary>
        ///  Clear lists, and reset pointer variables
        /// </summary>
        public void Dispose()
        {
            Multiset.Clear();
            Label = null;
            Rules.Clear();
            Configuration = null;
        }

        public override string ToString()
        {
            return String.Format("[{0}]", Label);
        }

        /// <summary>
        ///     Applying of a new solution, means to create a new membrane copy,
        /// with new configuration applyied on it
        /// </summary>
        /// <param name="solution"></param>
        /// <returns></returns>
        public MembranesTree ApplySolution(Solution solution, PSystemsTree parrentConfig)
        {

            solution.AppliedRule.ApplyRule(solution, parrentConfig);


            return null;
        }

    }
}